{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import random\n",
    "import math\n",
    "import os"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 全局配置plot参数\n",
    "plt.rcParams['font.sans-serif'] = 'SimHei'\n",
    "plt.rcParams['axes.unicode_minus'] = False\n",
    "plt.rcParams['figure.figsize'] = (4.0, 4.0)\n",
    "plt.rcParams['text.usetex'] = True\n",
    "plt.rcParams['text.latex.preamble'] = [r'\\usepackage{amsmath}']\n",
    "plt.rcParams['animation.html'] = 'jshtml'\n",
    "plt.rcParams['axes.labelsize'] = 16\n",
    "\n",
    "dataset_dir = '/home/lidong/Datasets/'"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Dataset](https://pan.baidu.com/s/1axooVu-QAV6uc-sl44AEwg) 提取码: vqk8"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def load_data_from_file(file):\n",
    "    result = []\n",
    "    with open(file) as fp:\n",
    "        for line in fp:\n",
    "            item = line.rstrip('\\n').split(',')\n",
    "            result.append(list(map(np.float32, item)))\n",
    "    return result \n",
    "\n",
    "def gen_random_value(rnd):\n",
    "    lo = -0.01\n",
    "    hi = 0.01\n",
    "    return (hi - lo) * rnd.random() + lo"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[5.1, 3.5, 1.4, 0.2, 1.0, 0.0, 0.0]"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "train_data = load_data_from_file(os.path.join(dataset_dir, 'ML/irisTrainData.txt'))\n",
    "test_data = load_data_from_file(os.path.join(dataset_dir, 'ML/irisTestData.txt'))\n",
    "train_data[0]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "class NeuralNetwork(object):\n",
    "    @staticmethod\n",
    "    def tanh(hs):\n",
    "        if hs < -20.0:\n",
    "            return -1.0\n",
    "        elif hs > 20.0:\n",
    "            return 1.0\n",
    "        else:\n",
    "            return math.tanh(hs)\n",
    "\n",
    "    @staticmethod\n",
    "    def softmax(os):\n",
    "        result = np.zeros(shape=[len(os)], dtype=np.float32)\n",
    "        m = max(os)\n",
    "        divisor = 0.0\n",
    "        for k in range(len(os)):\n",
    "          divisor += math.exp(os[k] - m)\n",
    "        for k in range(len(result)):\n",
    "          result[k] =  math.exp(os[k] - m) / divisor\n",
    "        return result\n",
    "    \n",
    "    def __init__(self, ni, nh, no):\n",
    "        self.ni = ni\n",
    "        self.nh = nh\n",
    "        self.no = no\n",
    "        \n",
    "        self.i_nodes = np.zeros(shape=[self.ni], dtype=np.float32)\n",
    "        self.h_nodes = np.zeros(shape=[self.nh], dtype=np.float32)\n",
    "        self.o_nodes = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        \n",
    "        self.ih_weigths = np.zeros(shape=[self.ni, self.nh], dtype=np.float32) # ni * nh\n",
    "        self.ho_weigths = np.zeros(shape=[self.nh, self.no], dtype=np.float32) # nh * no\n",
    "        \n",
    "        self.h_biases = np.zeros(shape=[self.nh], dtype=np.float32) # nh\n",
    "        self.o_biases = np.zeros(shape=[self.no], dtype=np.float32) # no\n",
    "        \n",
    "        # init weight value\n",
    "        self.rnd = random.Random(0)\n",
    "        for i in range(self.ni):\n",
    "            for j in range(self.nh):\n",
    "                self.ih_weigths[i, j] = gen_random_value(self.rnd)\n",
    "        \n",
    "        for j in range(self.nh):\n",
    "            for k in range(self.no):\n",
    "                self.ho_weigths[j, k] = gen_random_value(self.rnd)\n",
    "                \n",
    "        for j in range(self.nh):\n",
    "            self.h_biases[j] = gen_random_value(self.rnd)\n",
    "            \n",
    "        for k in range(self.no):\n",
    "            self.o_biases[k] = gen_random_value(self.rnd)\n",
    "            \n",
    "    def calculate_nodes_result(self, xs):\n",
    "        # input nodes\n",
    "        for i in range(self.ni):\n",
    "            self.i_nodes[i] = xs[i]\n",
    "            \n",
    "        # hiden nodes\n",
    "        for j in range(self.nh):\n",
    "            hsums = 0.0\n",
    "            for i in range(self.ni):\n",
    "                hsums += self.i_nodes[i] * self.ih_weigths[i, j]\n",
    "            hsums += self.h_biases[j]\n",
    "            self.h_nodes[j] = self.tanh(hsums)\n",
    "            \n",
    "        # output nodes\n",
    "        osums = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        for k in range(self.no):\n",
    "            for j in range(self.nh):\n",
    "                osums[k] += self.h_nodes[j] * self.ho_weigths[j, k]\n",
    "            osums[k] += self.o_biases[k]\n",
    "        self.o_nodes = self.softmax(osums)\n",
    "        return self.o_nodes\n",
    "    \n",
    "    def parse_input_target_values(self, data):\n",
    "        xvalues = np.zeros(shape=[self.ni], dtype=np.float32)\n",
    "        tvalues = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        for i in range(self.ni):\n",
    "            xvalues[i] = data[i]\n",
    "        for k in range(self.no):\n",
    "            tvalues[k] = data[self.ni + k]\n",
    "        return xvalues, tvalues\n",
    "\n",
    "    def train(self, data, lrate, epochs, loss_method='mcee'):\n",
    "        # init hiden layer gradient weigths and bias\n",
    "        ih_grads = np.zeros(shape=[self.ni, self.nh], dtype=np.float32)\n",
    "        hb_grads = np.zeros(shape=[self.nh], dtype=np.float32)\n",
    "        \n",
    "        # init output layer gradient of weigths  and biases\n",
    "        ho_grads = np.zeros(shape=[self.nh, self.no], dtype=np.float32)\n",
    "        ob_grads = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        \n",
    "        # init hiden and ouput layer middle calculate signals\n",
    "        h_signals = np.zeros(shape=[self.nh], dtype=np.float32)\n",
    "        o_signals = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        \n",
    "        # init input values and target values\n",
    "        # xvalues = np.zeros(shape=[self.ni], dtype=np.float32)\n",
    "        # tvalues = np.zeros(shape=[self.no], dtype=np.float32)\n",
    "        \n",
    "        errs = []\n",
    "        length = len(data)\n",
    "        indices = np.arange(length)\n",
    "        for epoch in range(1, epochs+1):\n",
    "            # self.rnd.shuffle(indices)\n",
    "            for ri in range(length):\n",
    "                idx = indices[ri]\n",
    "                \n",
    "                # eval xvalues and tvalues\n",
    "                xvalues, tvalues = self.parse_input_target_values(data[idx])\n",
    "                \n",
    "                # calculate the input, hiden, output nodes\n",
    "                self.calculate_nodes_result(xvalues)\n",
    "                \n",
    "                # 1. compute the o_signals\n",
    "                if loss_method == 'mse':\n",
    "                    for k in range(self.no):\n",
    "                        o_signals[k] = (tvalues[k] - self.o_nodes[k]) * (1 - self.o_nodes[k]) * self.o_nodes[k]\n",
    "                else:\n",
    "                    for k in range(self.no):\n",
    "                        o_signals[k] = (tvalues[k] - self.o_nodes[k]) * 1.0\n",
    "                \n",
    "                # 2. compute the ho_grads and ob_grads\n",
    "                for k in range(self.no):\n",
    "                    for j in range(self.nh):\n",
    "                        ho_grads[j, k] = o_signals[k] * self.h_nodes[j]\n",
    "                    ob_grads[k] = o_signals[k] * 1.0\n",
    "                \n",
    "                # 3. compute the h_signals\n",
    "                for j in range(self.nh):\n",
    "                    sum = 0.0\n",
    "                    for k in range(self.no):\n",
    "                        sum += o_signals[k] * self.ho_weigths[j, k]\n",
    "                    h_signals[j] = (1 - self.h_nodes[j]) * (1 + self.h_nodes[j]) * sum\n",
    "                    \n",
    "                # 4. compute the ih_grads and hb_grads\n",
    "                for j in range(self.nh):\n",
    "                    for i in range(self.ni):\n",
    "                        ih_grads[i, j] = h_signals[j] * self.i_nodes[i]\n",
    "                    hb_grads[j] = h_signals[j] * 1.0\n",
    "                    \n",
    "                # 5. update ih_weigths and h_biases\n",
    "                for j in range(self.nh):\n",
    "                    for i in range(self.ni):\n",
    "                        self.ih_weigths[i, j] += lrate * ih_grads[i, j]\n",
    "                    self.h_biases[j] += lrate * hb_grads[j]\n",
    "                    \n",
    "                # 6. update ho_weigths and o_biases\n",
    "                for k in range(self.no):\n",
    "                    for j in range(self.nh):\n",
    "                        self.ho_weigths[j, k] += lrate * ho_grads[j, k]\n",
    "                    self.o_biases[k] += lrate * ob_grads[k]\n",
    "            # end sample       \n",
    "            if loss_method == 'mse':\n",
    "                err = self.mse(data)\n",
    "            else:\n",
    "                err = self.mcee(data)\n",
    "            if epoch % 10 == 0:\n",
    "                print('epoch[%d]\\t%s err[%0.4f]' % (epoch, loss_method, err))\n",
    "            errs.append(err)\n",
    "        # end epoch\n",
    "        return errs \n",
    "                    \n",
    "    def mse(self, data):\n",
    "        # mean squared error\n",
    "        length = len(data)\n",
    "        sum_err = 0.0\n",
    "        for i in range(length):\n",
    "            xvalues, tvalues = self.parse_input_target_values(data[i])\n",
    "            yvalues = self.calculate_nodes_result(xvalues)\n",
    "            for k in range(self.no):\n",
    "                err = tvalues[k] - yvalues[k]\n",
    "                sum_err += err * err\n",
    "        return sum_err / length\n",
    "            \n",
    "    \n",
    "    def mcee(self, data):\n",
    "        # mean cross entropy error\n",
    "        length = len(data)\n",
    "        sum_err = 0.0\n",
    "        for i in range(length):\n",
    "            xvalues, tvalues = self.parse_input_target_values(data[i])\n",
    "            yvalues = self.calculate_nodes_result(xvalues)\n",
    "            # 1.0: tvalues is one-hot\n",
    "            sum_err += math.log(yvalues[np.argmax(tvalues)])\n",
    "        return -1.0 * sum_err / length\n",
    "                    \n",
    "    def accuracy(self, data):\n",
    "        num_wrong = 0; num_correct = 0\n",
    "        length = len(data)\n",
    "        for i in range(length):\n",
    "            xvalues, tvalues = self.parse_input_target_values(data[i])\n",
    "            yvalues = self.calculate_nodes_result(xvalues)\n",
    "            tval = tvalues[np.argmax(yvalues)]\n",
    "            if abs(tval - 1.0) < 0.0001:\n",
    "                num_correct += 1\n",
    "            else:\n",
    "                num_wrong += 1\n",
    "        return (num_correct * 1.0) / length"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch[10]\tmcee err[1.1016]\n",
      "epoch[20]\tmcee err[1.1917]\n",
      "epoch[30]\tmcee err[0.8595]\n",
      "epoch[40]\tmcee err[0.6127]\n",
      "epoch[50]\tmcee err[0.5086]\n",
      "epoch[60]\tmcee err[0.4309]\n",
      "epoch[70]\tmcee err[0.3752]\n",
      "epoch[80]\tmcee err[0.3363]\n",
      "epoch[90]\tmcee err[0.3090]\n",
      "epoch[100]\tmcee err[0.2894]\n",
      "train data accuracy: 0.883\n",
      "test  data accuracy: 0.900\n"
     ]
    }
   ],
   "source": [
    "nn = NeuralNetwork(4, 5, 3)\n",
    "mcess_data = nn.train(train_data, 0.005, 100, 'mcee')\n",
    "print('train data accuracy: %.3f' % nn.accuracy(train_data))\n",
    "print('test  data accuracy: %.3f' % nn.accuracy(test_data))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch[10]\tmse err[0.6651]\n",
      "epoch[20]\tmse err[0.6516]\n",
      "epoch[30]\tmse err[0.5892]\n",
      "epoch[40]\tmse err[0.4527]\n",
      "epoch[50]\tmse err[0.3656]\n",
      "epoch[60]\tmse err[0.3274]\n",
      "epoch[70]\tmse err[0.3041]\n",
      "epoch[80]\tmse err[0.2854]\n",
      "epoch[90]\tmse err[0.2689]\n",
      "epoch[100]\tmse err[0.2542]\n",
      "train data accuracy: 0.808\n",
      "test  data accuracy: 0.867\n"
     ]
    }
   ],
   "source": [
    "nn = NeuralNetwork(4, 5, 3)\n",
    "mse_data = nn.train(train_data, 0.005, 100, 'mse')\n",
    "print('train data accuracy: %.3f' % nn.accuracy(train_data))\n",
    "print('test  data accuracy: %.3f' % nn.accuracy(test_data))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.collections.PathCollection at 0x7f5f8e9dbf98>"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 288x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "xs = [i for i in range(len(mcess_data))]\n",
    "plt.scatter(xs, mcess_data)\n",
    "plt.scatter(xs, mse_data)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
